package sk.naive.talker.adapter;

import sk.naive.talker.*;
import sk.naive.talker.util.*;
import sk.naive.talker.props.*;

import java.rmi.*;
import java.rmi.server.*;
import java.util.*;
import java.util.logging.*;
import java.io.IOException;

/**
 * Skeletal implementation of RemoteAdapter interface.
 *
 * @author <a href="mailto:virgo@naive.deepblue.sk">Richard "Virgo" Richter</a>
 * @version $Revision: 1.34 $ $Date: 2005/01/06 20:54:27 $
 */
public abstract class AbstractAdapter extends UnicastRemoteObject
	implements Adapter
{
	/** Meno adaptera. */
	protected String name;

	/** Zoznam pouzivatelov tohto adaptera. */
	protected List<AbstractUser> users;

	/** Logged in users map. */
	protected Map<Integer, AbstractUser> idUser;

	/** Mapa tried pre jednotlive properties pouzivane v tomto adapteri. */
	protected Map propertyObject;

	protected RemoteTalker talker;

	/** Premenna pre realizaciu lazy shutdownu. */
	protected boolean shutdown = false;

	protected Logger logger;

	private PropertyConfiguration propertyConfiguration;
	private PropertyConfiguration userPropertyConfiguration;
	private Map<String, Object> properties;

	protected AbstractAdapter() throws RemoteException {
	}

	/**
	 * Adapter initialization.
	 * Registers adapter at talker server and creates logger.
	 * Should be called after name of this adapter is set.
	 * @param propertiesBundleName property configuration file name
	 */
	public void init(String propertiesBundleName, String userPropertiesBundleName)
		throws RemoteException,
		NotBoundException
	{
		properties = new HashMap<String, Object>();
		propertyConfiguration = new PropertyConfiguration(propertiesBundleName);
		userPropertyConfiguration = new PropertyConfiguration(userPropertiesBundleName);
		users = new LinkedList<AbstractUser>();
		idUser = new HashMap<Integer, AbstractUser>();
		talker = Main.getServer();
		getProperties().put(Adapter.APROP_CREATED, System.currentTimeMillis());
		RemotePropertyConfiguration rpc = new RemotePropertyConfiguration(propertyConfiguration, userPropertyConfiguration);
		talker.registerAdapter(this, rpc);
		logger = Logger.getLogger("nt.adapter." + name);
		logger.info("Adapter registered at talker kernel.");
	}

	/**
	 * Spusta shutdown adaptera.
	 * Odregistruje objekt, odexportuje RMI objekt a nastavi priznak shutdownu.
	 * Ten si uz musia konkretne adaptery checknut sami.
	 *
	 * @throws java.rmi.RemoteException
	 */
	public void shutdown()
		throws IOException
	{
		try {
			for (Iterator<AbstractUser> i = users.iterator(); i.hasNext(); ) {
				AbstractUser user = i.next();
				i.remove();
				user.disconnect("adapter-shutdown");
			}
			talker.deregisterAdapter(this);
			unexportObject(this, true);
			logger.info("Adapter unregistered & unexported.");
		} catch (Exception e) {
			e.printStackTrace();
		}
		shutdown = true;
	}

	protected AbstractUser userFromRemote(AdapterUser u) throws RemoteException {
		if (u instanceof AbstractUser) {
			return (AbstractUser) u;
		}
		Integer id = (Integer) u.get(User.UPROP_ID);
		for (Object user1 : users) {
			AbstractUser user = (AbstractUser) user1;
			Integer testedId = user.getId();
			if (testedId != null && testedId.equals(id)) {
				return user;
			}
		}
		return null;
	}

	public final void disconnect(AdapterUser u)
		throws RemoteException
	{
		AbstractUser user = userFromRemote(u);
		if (user != null && users.remove(user)) {
			UnicastRemoteObject.unexportObject(user, true);
			user.disconnectCleanup();
			disconnectAbstractUser(user);
			logger.finer("User." + user.getId() + " disconnected from adapter.");
		} else {
			logger.warning("User " + u + " not connected to this adapter!");
		}
	}

	protected abstract void disconnectAbstractUser(AbstractUser user) throws RemoteException;

	/** Vrati meno adaptera. */
	public String getName()
		throws RemoteException
	{
		return name;
	}

	public Map<String, Object> getProperties()
		throws RemoteException
	{
		return properties;
	}

	public Object get(String key)
		throws RemoteException
	{
		Object rval = properties.get(key);
		if (rval == null) {
			return getProperty(key).defaultValue();
		}
		return rval;
	}

	public void set(String key, Object value)
		throws RemoteException,
		InvalidPropertyValueException
	{
		Property p = getProperty(key);
		if (p != null) {
			if (!p.checkValue(value)) {
				throw new InvalidPropertyValueException("Invalid value '" + value + "' for property " + key + ".");
			}
		}
		properties.put(key, value);
	}

	public Property getProperty(String key) {
		return propertyConfiguration.getProperty(key);
	}

	public PropertyConfiguration getUserPropertyConfiguration() {
		return userPropertyConfiguration;
	}
}
